package org.swift.mashup.engine.concurrency.execution;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.swift.mashup.engine.concurrency.memory.MemoryItem;
import org.swift.mashup.engine.concurrency.memory.MemoryType;





public class RulePattern {
	private static Log log = LogFactory.getLog(RulePattern.class);
	private int id;
	private Map<String, MemoryType> types = new HashMap<String, MemoryType>();
	private Map<BitSet, ConditionPattern> conditionPatterns = new HashMap<BitSet, ConditionPattern>();
	private Map<String, Condition> conditions = new HashMap<String, Condition>();
	private BitSet mask = new BitSet();
	private int conditionCount = 0;
	private int conditionPatternCount = 0;
	
	public RulePattern(){
	}
	public RulePattern(int id){
		this.setId(id);
		this.setMask(new BitSet());
		this.getMask().set(id);
	}
	public RulePattern(int id, BitSet mask){
		this.setId(id);
		this.setMask(mask);
	}
	public RulePattern(int id, MemoryType type){
		this.setId(id);
		this.setMask(type.getMask());
		this.getTypes().put(type.getName(), type);
	}
	public RulePattern(int id, Collection<MemoryType> types){
		this.setId(id);
		this.setMask(computeTypeMask(types));
		for (MemoryType type:types){
			this.types.put(type.getName(), type);
		}
	}
	private BitSet computeTypeMask(Collection<MemoryType> types){
		BitSet mask = new BitSet();
		for (MemoryType type:types) {
			mask.or(type.getMask());
		}
		return mask;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public Map<String, MemoryType> getTypes() {
		return types;
	}
	public void setTypes(Map<String, MemoryType> types) {
		this.types = types;
	}
	public Set<Rule> getRules() {
		Set<Rule> rules = new HashSet<Rule>();
		for (ConditionPattern pattern: this.conditionPatterns.values()){
			rules.addAll(pattern.getRules());
		}
		return rules;
	}
	public Collection<Rule> getActiveRules(Map<String, MemoryItem> parameterValues){
		Set<Rule> rules = new HashSet<Rule>();
	//	log.debug("\nPattern [" + this.getId() + "] evaluated for params [" + parameterValues + "]");
		BitSet activeMask = evaluate(parameterValues);
		for (ConditionPattern pattern: this.conditionPatterns.values()){
			BitSet currentMask = new BitSet();
			currentMask.or(pattern.getMask());
			currentMask.and(activeMask);
			if (currentMask.equals(pattern.getMask())){
				rules.addAll(pattern.getRules());				
			}
		}
		return rules;
	}
	public void addRule(Rule rule) {
		List<Condition> conditions = new ArrayList<Condition>();
		for (Condition condition: rule.getConditions()){
			conditions.add(addCondition(condition,rule.getParameters()));
		}
		ConditionPattern pattern = addConditionPattern(conditions);
		pattern.getRules().add(rule);
	}
	private ConditionPattern addConditionPattern(List<? extends Condition> conditions){
		ConditionPattern pattern = null;
		BitSet mask = ConditionPattern.computeConditionMask(conditions);
		if (this.conditionPatterns.containsKey(mask)){
			pattern = this.conditionPatterns.get(mask);
		}else{
			pattern = new ConditionPattern(++conditionPatternCount,mask,this);
			this.conditionPatterns.put(mask, pattern);		
		}
		return pattern;
	}
	private Condition addCondition(Condition condition, Map<String, Parameter> parameterMap){
		String expression = condition.getExpression();

		//Replace all local variable names with the global working memory names
		for (Parameter parameter:parameterMap.values()){
			expression = expression.replaceAll("\\$" + parameter.getName(), "\\$" + parameter.getValue());
		}
		//Trim out all spaces except between ''s
		// 	Need to come up with a better expression for this... because the expression below
		// 	can turn an valid XQuery expression into an invalid one.
		// 		replaceAll(" ++(?=(?:[^']*+'[^']*+')*+[^']*+$)", "");
		expression = expression.trim();
		if (this.conditions.containsKey(expression)) return this.conditions.get(expression);
		
		BitSet mask = new BitSet();
		mask.set(++conditionCount);
		
		Condition newCondition = new Condition(expression,this.getTypeSet(),mask,condition.getNamespaces());

		this.conditions.put(expression, newCondition);
		return newCondition;
	}
	private Collection<String> getTypeSet(){
		Collection<String> typeSet = new HashSet<String>();
		for (MemoryType type:this.getTypes().values()){
			typeSet.add(type.getName());
		}
		return typeSet;
	}
	public BitSet evaluate(Map<String, MemoryItem> parameterValues){
		BitSet conditionMask = new BitSet();
		for (Condition condition: this.conditions.values()){
			boolean match = condition.evaluate(parameterValues);
			if (match){
				conditionMask.or(condition.getMask());				
			}
			log.debug("\nCondition [" + condition.getExpression() + "] = " + match);
		}
		return conditionMask;
	}
	public BitSet getMask() {
		return mask;
	}
	public void setMask(BitSet mask) {
		this.mask = mask;
	}
	
}
